8

阿里一面是电话面,问得不多,但是挺有深度。面试官一开始就说,看了你的项目,觉得你基础挺好的,那我就不问基础了。然后全程就真的没有问一个基础问题。。

1.说说你做的那个网页版手机QQ项目的难点。

我首先想到了滚动条位置无法还原的问题,也就是点击列表项跳到详情页后再返回时,滚动条位置无法还原的bug。我的解决办法是通过vuex来保存每个组件里的滚动条的位置,然后返回时再滚动到这个位置即可,不过得设置滚动时间为0,不然会有明显的滚动过程,用户体验会很差。
然后又想到了做IP定位时遇到的后台express服务端无法获取用户真实ip地址的问题。其实express是可以获取用户真实ip地址的,只是我的nginx没有写好配置,导致express获取到的始终是127.0.0.1本地ip地址。无论怎么获取,都是这个地址。
解决办法是修改nginx的配置文件:

location / {
   proxy_pass http://localhost:8080;   # 项目真实地址
  proxy_set_header Host $host;
   proxy_set_header X-Real-IP $remote_addr;
   proxy_set_header REMOTE-HOST $remote_addr;
   proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

重启nginx: ./nginx -s reload 即可。
然后在express服务端里,通过 req.headers[‘x-real-ip’]即可拿到真实ip地址。然后就可以根据这个ip地址,通过淘宝ip地址库,定位到用户的地理位置了。
QQ项目地址:https://github.com/lensh/vue-qq (支持实时群聊、实时私聊、特别关心、屏蔽聊天、IP地位、实时气温显示等QQ核心功能)

2.Angular和Vue的双向数据绑定实现的原理? 如果要你来实现,你会怎样实现?

还好之前对底层还是有所了解,看过源码以及一些博客,所以基本答了出来。
Angular的实现: AngularJS 采用“脏值检测”的方式,数据发生变更后,对于所有的数据和视图的绑定关系进行一次检测,识别是否有数据发生了改变,有变化进行处理,可能进一步引发其他数据的改变,所以这个过程可能会循环几次,一直到不再有数据变化发生后,将变更的数据发送到视图,更新页面展现。
只有当改变$scope的值、使用内置的$interval、$timeout的时候,才会进行“脏检测”。
如果是手动对 ViewModel 的数据进行变更,为确保变更同步到视图,需要手动触发一次“脏值检测”。
Vue的实现:核心就是数据劫持+发布/订阅模式,VueJS 使用 ES5 提供的 Object.defineProperty() 方法,监控对数据的操作,从而可以自动触发数据同步。并且,由于是在不同的数据上触发同步,可以精确的将变更发送给绑定的视图,而不是对所有的数据都执行一次检测。
具体实现可参考 https://github.com/lensh/mvvm

3.webpack打包后文件体积过大怎么办?

很多方法:异步加载模块(代码分割);提取第三方库(使用cdn或者vender);代码压缩;去除不必要的插件;去除devtool选项等等。

4.看你还写了一个爬虫项目,那如果在爬取数据的时候遇到环路了怎么办,怎样避免?

避免方法也很多:简单限定爬虫的最大循环次数,对于某web站点访问超过一定阈值就跳出,避免无限循环。保存一个已访问url列表,记录页面是否被访问过的技术。广度优先的爬行,避免深度优先陷入某个站点的环路中,无法访问其他站点。

5.React组件性能优化?immutable.js实现原理?

使用PureRenderMixin、shouldComponentUpdate来避免不必要的虚拟DOM diff,在render内部优化虚拟DOM的diff速度,以及让diff结果最小化。使用immutable.js解决复杂数据diff、clone等问题。
immutable.js实现原理:持久化数据结构,也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变。同时为了避免 deepCopy 把所有节点都复制一遍带来的性能损耗,Immutable 使用了结构共享,即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。

6.有没有用过Redux,Redux-promise和Redux-trunk是用来干嘛的,区别是什么?

明显是处理异步操作的啊,前者是让Redux的dispatch方法的参数支持promise,后者是支持函数,而且还是一个异步函数。异步函数里面要dispatch两次,分别表示异步请求开始和异步请求完成。

7.了解ES8吗?说说getOwnPropertyDescriptors函数,对象自身属性描述符有哪些?

configurable和enumerable的作用?configurable设置为true后还能删除该属性吗?
之前研究过这个,所以就很容易地回答了出来。该函数返回指定对象(参数)的所有自身属性描述符。所谓自身属性描述符就是在对象自身内定义,不是通过原型链继承来的属性。
属性描述符:configurable、enumerable、value、writable、get、set。
configurable:当该值为true时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为 false。
enumerable:当且仅当该属性的 enumerable 为 true时,该属性才能够出现在对象的枚举属性中。默认为 false。

8.Promise.all和Promise.race的区别?

Promise.all 把多个promise实例当成一个promise实例,当这些实例的状态都发生改变时才会返回一个新的promise实例,才会执行then方法。
Promise.race 只要该数组中的 Promise 对象的状态发生变化(无论是resolve还是reject)该方法都会返回。

9.只用CSS怎样实现标签页的切换效果?

利用锚点结合CSS的target伪类就可以了。

10.mod_gzip 和mod_deflate的主要区别是什么?使用哪个更好呢?

首先一个区别是安装它们的Apache Web服务器版本的差异。Apache 1.x系列没有内建网页压缩技术,所以才去用额外的第三方mod_gzip 模块来执行压缩。而Apache 2.x官方在开发的时候,就把网页压缩考虑进去,内建了mod_deflate 这个模块,用以取代mod_gzip。虽然两者都是使用的Gzip压缩算法,它们的运作原理是类似的。
第二个区别是压缩质量。mod_deflate 压缩速度略快而mod_gzip 的压缩比略高。一般默认情况下,mod_gzip 会比mod_deflate 多出4%~6%的压缩量。
第三个区别是对服务器资源的占用。 一般来说mod_gzip 对服务器CPU的占用要高一些。mod_deflate 是专门为确保服务器的性能而使用的一个压缩模块,mod_deflate 需要较少的资源来压缩文件。这意味着在高流量的服务器,使用mod_deflate 可能会比mod_gzip 加载速度更快。

11.nodejs子进程 spawn,exec,execFile和fork的用法和区别?

spawn函数用给定的命令发布一个子进程,只能运行指定的程序,参数需要在列表中给出。
exec也是一个创建子进程的函数,与spawn函数不同它可以直接接受一个回调函数作为参数,回调函数有三个参数,分别是err, stdout , stderr。
execFile函数与exec函数类似,但execFile函数更显得精简,因为它可以直接执行所指定的文件。
fork函数可直接运行Node.js模块,我们可以直接通过指定模块路径而直接进行操作。

12.nodejs中定时器process.nextTick和setImmediate的区别,优先级。

process.nextTick()的回调函数执行的优先级要高于setImmediate(),process.nextTick()属于idle观察者。
setImmediate()属check观察者.在每一轮循环检查中,idle观察者先于I/O观察者,I/O观察者先于check观察者。
process.nextTick()的回调函数保存在一个数组中,会将异步回调放到当前帧的末尾、io回调之前,如果nextTick过多,会导致io回调不断延后,最后callback堆积太多。
setImmediate()的结果则是保存在链表中,会将异步回调放到下一帧,不影响io回调,不会造成callback 堆积。
process.nextTick()在每轮循环中会将数组中的回调函数全部执行完,而setImmediate()在每轮循环中执行链表中的一个回调函数。
process.nextTick(),效率最高,消费资源小,但会阻塞CPU的后续调用;
setTimeout(),精确度不高,可能有延迟执行的情况发生,且因为动用了红黑树,所以消耗资源大;
setImmediate(),消耗的资源小,也不会造成阻塞,但效率也是最低的。

13.Koa2和Koa1的区别,和express的区别?

(1)异步流程控制
Express 采用 callback 来处理异步,Koa v1 采用 generator,Koa v2 采用 async/await。
generator 和 async/await 使用同步的写法来处理异步,明显好于 callback 和 promise,async/await 在语义化上又要比 generator 更强。
(2)错误处理
Express 使用 callback 捕获异常,对于深层次的异常捕获不了,Koa 使用 try catch,能更好地解决异常捕获。

14.一行代码实现数组去重?

[…new Set([1,2,3,1,’a’,1,’a’])]

15.http2.0的优势?

当时大概地讲了一下意思,不过没答完全。后了又复习了下:
(1)采用二进制格式传输数据,而非 http1.1 的文本格式,二进制格式在协议的解析和优化扩展上带来更多的优势和可能
(2)对消息头采用 HPACK 进行压缩传输,能够节省消息头占用的网络的流量,而 http1.1 每次请求,都会携带大量冗余头信息,浪费了很多带宽资源,头压缩能够很好的解决该问题
(3)多路复用,就是多个请求都是通过一个 TCP 连接并发完成,http1.1 虽然通过pipeline也能并发请求,但是多个请求之间的响应会被阻塞的,所以 pipeline 至今也没有被普及应用,而 http2.0做到了真正的并发请求,同时,流还支持优先级和流量控制
(4)Server Push,服务端能够更快的把资源推送给客户端,例如服务端可以主动把 JS 和 CSS 文件推送给客户端,而不需要客户端解析 HTML再发送这些请求,当客户端需要的时候,它已经在客户端了。

16.什么是BFC,作用有哪些?哪些情况下会触发BFC?

当时触发条件只答出了1、2、4、5,而且4只说了inline-block。这种很多情况的一时半会确实很难答完全。。
完整答案:
BFC(块级格式化上下文),是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面元素,反之亦然。它与普通的块框类似,但不同之处在于:
(1)可以阻止元素被浮动元素覆盖。
(2)可以包含浮动元素。
(3)可以阻止margin重叠。
满足下列条件之一就可触发BFC:
【1】根元素,即HTML元素
【2】float的值不为none
【3】overflow的值不为visible
【4】display的值为inline-block、table-cell、table-caption
【5】position的值为absolute或fixed

17.304缓存,有了Last-Modified,为什么还要用ETag?有了Etag,为什么还要用Last-Modified?Etag一般怎么生成?

有了Last-Modified,为什么还要用ETag?
(1)因为如果在一秒钟之内对一个文件进行两次更改,Last-Modified就会不正确。
(2)某些服务器不能精确的得到文件的最后修改时间。
(3)一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新GET。

有了Etag,为什么还要用Last-Modified?
因为有些时候 ETag 可以弥补 Last-Modified 判断的缺陷,但是也有时候 Last-Modified 可以弥补 ETag 判断的缺陷,比如一些图片等静态文件的修改,如果每次扫描内容生成 ETag 来比较,显然要比直接比较修改时间慢很多。所有说这两种判断是相辅相成的。

ETag的值服务端是对文件的索引节,大小和最后修改时间进行Hash后得到的。

18.你有什么要问的吗?

大概就只记得问了这些,大部分都回答了出来。当然回答时还扯了一些其它的,就不说了。总体来说还是挺有深度的,日后还得继续努力。
最后,我的github:https://github.com/lensh


莫知我哀
184 声望20 粉丝